const body = document.querySelector('body');
body.style.display = 'flex';
body.style.width = '100vw';
body.style.height = '100vh';
body.style.alignItems = 'center';
body.style.justifyContent = 'center';
body.style.perspective = '888px';

// 虚拟DOM，提高渲染效率
const fg = document.createDocumentFragment();

// 不可拖拽区域
const area = document.createElement('div');
area.style.position = 'absolute';
area.style.width = '777rem';
area.style.height = '777rem';
area.style.zIndex = '-777';
area.style.borderRadius = '50%';
area.style.backgroundColor = 'rgba(0, 0, 0, .1)';
area.style.transition = 'all .2s ease';
fg.appendChild(area);

// 中心定位
const center = document.createElement('div');
center.style.position = 'absolute';
center.style.transformStyle = 'preserve-3d';

let angleX = 60, angleY = -30;
center.style.transform = `rotateX(${angleY}deg) rotateY(${angleX}deg)`;

// 拖拽区域
const screen = document.createElement('div');
screen.style.position = 'absolute';
screen.style.width = '100%';
screen.style.height = '100%';
screen.style.zIndex = '-999';

fg.appendChild(center);
fg.appendChild(screen);

body.appendChild(fg);

// 旋转中心
const rotate = document.createElement('div');
rotate.style.position = 'absolute';
rotate.style.transformStyle = 'preserve-3d';

center.appendChild(rotate);

// 防止出现拖拽BUG
let turning = false;
let oldX, oldY, rotateAngleX = 0, rotateAngleY = 0;

// 用于操作撤销、前进
let baseX, baseY, rotateBaseX = 0, rotateBaseY = 0;
let gapRotateX, gapRotateY;
let isLR = false;

screen.addEventListener('mousedown', e => {
    if (turning || clickFlag) return;
    rotateBaseY = 0;
    rotateBaseX = 0;
    turning = true;
    oldX = e.x;
    oldY = e.y;
    baseX = e.x;
    baseY = e.y;
    screen.style.zIndex = '999';
    screen.addEventListener('mousemove', rotateCenter);
})

// 旋转控制方向
function rotateCenter(e) {
    area.style.backgroundColor = 'rgba(255, 0, 0, .1)';
    let gapX = e.x - oldX;
    let gapY = e.y - oldY;
    rotateBaseX = e.x - baseX;
    rotateBaseY = e.y - baseY;
    // 旋转方向拖拽像素阈值
    let thresholdValue = 5;
    // 绑定旋转方向
    if(!rotate.rotateBy) {
        if(Math.abs(gapX) >= thresholdValue) {
            rotate.rotateBy = rotateCenterByY;
            rotate.rotateText = 'ByY';
        }else if(Math.abs(gapY) >= thresholdValue){
            rotate.rotateBy = rotateCenterByX;
            rotate.rotateText = 'ByX';
        }
    }else {
        oldX = e.x;
        oldY = e.y;
        rotate.rotateBy(gapX, gapY);
    }
};

function rotateCenterByX(gapX, gapY) {
    rotateAngleY = (rotateAngleY + gapY) % 360;
    rotate.style.transform = `rotateX(${-rotateAngleY}deg)`;
}

function rotateCenterByY(gapX, gapY) {
    rotateAngleX = (rotateAngleX + gapX) % 360;
    rotate.style.transform = `rotateY(${rotateAngleX}deg)`;
}

screen.addEventListener('mouseup', removeRotate);
screen.addEventListener('mouseout', removeRotate);

function removeRotate() {
    if (!turning) return;
    // turning = false;
    screen.style.zIndex = '-999';
    screen.removeEventListener('mousemove', rotateCenter);
    rotate.style.transition = 'all .2s ease-in-out';

    gapRotateX = Math.round(rotateBaseY / 90) % 4;
    gapRotateY = Math.round(rotateBaseX / 90) % 4;

    if (gapRotateX == 0 && gapRotateY == 0) turning = false;
    // 相对回正
    rotateAngleX = Math.round(rotateAngleX / 90) * 90;
    rotateAngleY = Math.round(rotateAngleY / 90) * 90;

    rotate.style.transform = `rotateX(${-rotateAngleY}deg) rotateY(${rotateAngleX}deg)`;
}
// 防止BUG
rotate.addEventListener('transitionstart', () => {
    area.style.backgroundColor = 'rgba(111, 255, 111, .2)';
})
// 视觉变换,子块换位
rotate.addEventListener('transitionend', () => {
    rotate.rotateBy = undefined;
    rotate.rotateText = undefined;
    rotate.style.transition = null;
    area.style.backgroundColor = 'rgba(0, 0, 0, .1)';
    let angleX = isLR ? gapRotateY * -1 : rotateAngleX / 90;
    let angleY = isLR ? gapRotateX * -1 : rotateAngleY / 90;
    setTimeout(() => {
        turning = false;
    }, 0)
    if (angleX == 0 && angleY == 0) return;
    if(angleX == 0) {
        changeEverythingByX(angleY);
    }else if(angleY == 0) {
        changeEverythingByY(angleX);
    }
})

function changeRotateByX(angleX, angleY) {
    rotate.style.transition = 'all .2s ease-in-out';
    rotate.style.transform = `rotateX(${-angleX}deg)`;
}

function changeRotateByY(angleX, angleY) {
    rotate.style.transition = 'all .2s ease-in-out';
    rotate.style.transform = `rotateY(${-angleY}deg)`;
}

function changeEverythingByX(angle) {
    if(angle != 0) {
        for (let i = 0; i < 3; i++) {
            const even = [{y: 0, z: 0}, {y: 0, z: 2}, {y: 2, z: 2}, {y: 2, z: 0}];
            const odd = [{y: 0, z: 1}, {y: 1, z: 2}, {y: 2, z: 1}, {y: 1, z: 0}];
            const parity = [even, odd];
            rotate.childNodes.forEach( el => {
                if (el.position.x != i) return;
                // 改变eachCenter的position
                let stamp = (el.position.y + el.position.z) % 2;
                let item = parity[stamp].find( item => {
                    return item.y == el.position.y && item.z == el.position.z;
                })
                // 排除中心点
                if(item) {
                    let index = parity[stamp].indexOf(item);
                    el.position.y = parity[stamp][(index + angle + 4) % 4].y;
                    el.position.z = parity[stamp][(index + angle + 4) % 4].z;
                }
                el.style.transform =
                    `translate3d(${coordinate[el.position.x]}rem, ${coordinate[el.position.y]}rem, ${coordinate[el.position.z]}rem)`;
                // 改变子面
                el.childNodes.forEach(item => {
                    let index = XSide.indexOf(item.direction);
                    if (index != -1) {
                        item.direction = XSide[(index - angle + 4) % 4];
                        item.style.transform = sideType[item.direction].transform;
                    }
                })
                setTimeout(()=> {
                    el.style.transformOrigin = `${-coordinate[el.position.x]}rem ${-coordinate[el.position.y]}rem ${-coordinate[el.position.z]}rem`;
                    isLR = false;
                }, 0)
            })
        }
        rotateAngleX = 0;
        rotateAngleY = 0;
        rotate.style.transform = `rotateX(${-rotateAngleY}deg) rotateY(${rotateAngleX}deg)`;    
    }
}

function changeEverythingByY(angle) {
    if(angle != 0) {
        for (let i = 0; i < 3; i++) {
            const even = [{x: 0, z: 0}, {x: 0, z: 2}, {x: 2, z: 2}, {x: 2, z: 0}];
            const odd = [{x: 0, z: 1}, {x: 1, z: 2}, {x: 2, z: 1}, {x: 1, z: 0}];
            const parity = [even, odd];
            rotate.childNodes.forEach(el => {
                if(el.position.y != i) return;
                // 改变eachCenter的position
                let stamp = (el.position.x + el.position.z) % 2;
                let item = parity[stamp].find( item => {
                    return item.x == el.position.x && item.z == el.position.z;
                })
                // 排除中心点
                if(item) {
                    let index = parity[stamp].indexOf(item);
                    el.position.x = parity[stamp][(index + angle + 4) % 4].x;
                    el.position.z = parity[stamp][(index + angle + 4) % 4].z;
                }
                el.style.transform =
                    `translate3d(${coordinate[el.position.x]}rem, ${coordinate[el.position.y]}rem, ${coordinate[el.position.z]}rem)`;
                // 改变子面
                el.childNodes.forEach(item => {
                    let index = YSide.indexOf(item.direction);
                    if (index != -1) {
                        item.direction = YSide[(index + angle + 4) % 4];
                        item.style.transform = sideType[item.direction].transform;
                    }
                })
                setTimeout(()=> {
                    el.style.transformOrigin = `${-coordinate[el.position.x]}rem ${-coordinate[el.position.y]}rem ${-coordinate[el.position.z]}rem`;
                    isLR = false;
                }, 0)
            })
        }
        rotateAngleX = 0;
        rotateAngleY = 0;
        rotate.style.transform = `rotateX(${-rotateAngleY}deg) rotateY(${rotateAngleX}deg)`;
    }
}
// <--------------------------------------魔方-------------------------------------->
// 魔方建模
const coordinate = [-100, 0, 100];

const EachSide = ['front', 'back', 'left', 'right', 'top', 'bottom'];

const sideType = {
    front: {
        transform: 'translate3d(-50%, -50%, 50rem)',
        backgroundColor: 'white'
    },
    back: {
        transform: 'translate3d(-50%, -50%, -50rem)',
        backgroundColor: 'yellow'
    },
    left: {
        transform: 'translate3d(-100%, -50%, 0) rotateY(90deg)',
        backgroundColor: 'red'
    },
    right: {
        transform: 'translate3d(0, -50%, 0) rotateY(90deg)',
        backgroundColor: 'orange'
    },
    top: {
        transform: 'translate3d(-50%, -100%, 0) rotateX(90deg)',
        backgroundColor: 'green'
    },
    bottom: {
        transform: 'translate3d(-50%, 0, 0) rotateX(90deg)',
        backgroundColor: 'blue'
    },
}

const YSide = ['front', 'right', 'back', 'left'];
const XSide = ['front', 'top', 'back', 'bottom'];
const ZSide = ['left', 'top', 'right', 'bottom'];
const turnSide = {
    XSide,
    YSide,
    ZSide
}

for (let x = 0; x < 3; x++) {
    for (let y = 0; y < 3; y++) {
        for (let z = 0; z < 3; z++) {
            // 创建子块
            let eachCenter = createEach(x, y, z);
            rotate.appendChild(eachCenter);
        }
    }
}
// 创建子块
function createEach(x, y, z) {
    const eachCenter = document.createElement('div');
    eachCenter.angle = 0;
    eachCenter.style.position = 'absolute';
    eachCenter.style.transformStyle = 'preserve-3d';
    eachCenter.addEventListener('transitionstart', () => {
        area.style.backgroundColor = 'rgba(111, 255, 111, .2)';
    })
    eachCenter.addEventListener('transitionend', tEnd.bind(eachCenter));
    // 子块定位
    eachCenter.style.transform = `translate3d(${coordinate[x]}rem, ${coordinate[y]}rem, ${coordinate[z]}rem)`;
    eachCenter.style.transformOrigin = `${-coordinate[x]}rem ${-coordinate[y]}rem ${-coordinate[z]}rem`;
    // 子块面
    for (let i = 0; i < EachSide.length; i++) {
        eachCenter.appendChild(createEachSide(EachSide[i], x, y, z));
    }
    eachCenter.position = { x, y, z };
    return eachCenter;
}

// 重定位
function tEnd() {
    this.style.transition = null;
    this.style.transform = `translate3d(${coordinate[this.position.x]}rem, ${coordinate[this.position.y]}rem, ${coordinate[this.position.z]}rem)`;
    this.style.transformOrigin = `${-coordinate[this.position.x]}rem ${-coordinate[this.position.y]}rem ${-coordinate[this.position.z]}rem`;
    this.childNodes.forEach(el => {
        if(el.angle != 0) {
            let index = turnSide[el.turnSide].indexOf(el.direction);
            if (index != -1) {
                el.direction = turnSide[el.turnSide][(index + el.angle + 4) % 4];
                el.style.transform = sideType[el.direction].transform;
            }
            el.angle = 0;
        }
    })
    setTimeout(() => {
        turning = false;
    }, 0)
}

// 创建子块每个面
function createEachSide(side, x, y, z) {
    const eachSide = document.createElement('div');
    eachSide.style.position = 'absolute';
    eachSide.style.width = '100rem';
    eachSide.style.height = '100rem';
    eachSide.style.border = '5rem solid black';
    eachSide.style.transform = sideType[side].transform;
    eachSide.style.backgroundColor = sideType[side].backgroundColor;
    eachSide.direction = side;
    eachSide.angle = 0;
    eachSide.turnSide = null;
    eachSide.coordinate = { x, y, z };
    eachSide.addEventListener('click', sideMouseClick)
    return eachSide;
}

let clickFlag = false;
// 用于旋转层确定
let beforePosition = {}, afterPosition = {};
let beforeDirection = {}, afterDirection = {};

function sideMouseClick(e) {
    if (!clickFlag && !turning) {
        turning = true;
        area.style.backgroundColor = 'rgba(255, 0, 0, .1)';
        beforePosition = this.parentNode.position;
        beforeDirection = this.direction;
        clickFlag = !clickFlag;
    }else if (clickFlag) {
        area.style.backgroundColor = 'rgba(0, 0, 0, .1)';
        afterPosition = this.parentNode.position;
        afterDirection = this.direction;
        clickFlag = !clickFlag;
        // // 确保同面操作
        if(beforeDirection != afterDirection) return;
        // // 旋转轴确认，旋转方向确认
        let xGap = afterPosition.x - beforePosition.x;
        let yGap = afterPosition.y - beforePosition.y;
        let zGap = afterPosition.z - beforePosition.z;
        // 正面操作
        if(beforeDirection == 'front') {
            if(xGap == 0 && yGap != 0) {
                rotateByX(yGap > 0 ? 1 : -1, beforePosition.x);
            }else if(xGap != 0 && yGap == 0) {
                rotateByY(xGap > 0 ? 1 : -1, beforePosition.y);
            }
        }else if(beforeDirection == 'left') {
            if(zGap == 0 && yGap != 0) {
                rotateByZ(yGap > 0 ? -1 : 1, beforePosition.z);
            }else if(zGap != 0 && yGap == 0) {
                rotateByY(zGap > 0 ? 1 : -1, beforePosition.y);
            }
        }else if(beforeDirection == 'top') {
            if(zGap == 0 && xGap != 0) {
                rotateByZ(xGap > 0 ? 1 : -1, beforePosition.z);
            }else if(yGap == 0 && zGap != 0) {
                rotateByX(zGap > 0 ? 1 : -1, beforePosition.x);
            }
        }else {
            console.log('出大错');
        }
    }
};


// 绕X轴旋转
function rotateByX(angle, x) {
    const even = [{y: 0, z: 0}, {y: 0, z: 2}, {y: 2, z: 2}, {y: 2, z: 0}];
    const odd = [{y: 0, z: 1}, {y: 1, z: 2}, {y: 2, z: 1}, {y: 1, z: 0}];
    const parity = [even, odd];
    center.childNodes[0].childNodes.forEach( el => {
        if (el.position.x != x) return;
        // 旋转
        el.style.transition = 'all .5s ease';
        el.angle = angle;
        el.childNodes.forEach(element => {
            element.angle = angle * -1;
            element.turnSide = 'XSide';
        })
        el.style.transform =
            `translate3d(${coordinate[el.position.x]}rem, ${coordinate[el.position.y]}rem, ${coordinate[el.position.z]}rem) rotateX(${-el.angle * 90}deg)`;
        // // 改变eachCenter的position
        let stamp = (el.position.y + el.position.z) % 2;
        let item = parity[stamp].find( item => {
            return item.y == el.position.y && item.z == el.position.z;
        })
        // // 排除中心点
        if(item) {
            let index = parity[stamp].indexOf(item);
            el.position.y = parity[stamp][(index + angle + 4) % 4].y;
            el.position.z = parity[stamp][(index + angle + 4) % 4].z;
        }
    })
}
// 绕Y轴旋转
function rotateByY(angle, y) {
    const even = [{x: 0, z: 0}, {x: 0, z: 2}, {x: 2, z: 2}, {x: 2, z: 0}];
    const odd = [{x: 0, z: 1}, {x: 1, z: 2}, {x: 2, z: 1}, {x: 1, z: 0}];
    const parity = [even, odd];
    center.childNodes[0].childNodes.forEach( el => {
        if(el.position.y != y) return;
        // 旋转
        el.style.transition = 'all .5s ease';
        el.angle = angle;
        el.childNodes.forEach(element => {
            element.angle = angle;
            element.turnSide = 'YSide';
        })
        el.style.transform =
            `translate3d(${coordinate[el.position.x]}rem, ${coordinate[el.position.y]}rem, ${coordinate[el.position.z]}rem) rotateY(${el.angle * 90}deg)`;
        // // 改变eachCenter的position
        let stamp = (el.position.x + el.position.z) % 2;
        let item = parity[stamp].find( item => {
            return item.x == el.position.x && item.z == el.position.z;
        })
        // 排除中心点
        if(item) {
            let index = parity[stamp].indexOf(item);
            el.position.x = parity[stamp][(index + angle + 4) % 4].x;
            el.position.z = parity[stamp][(index + angle + 4) % 4].z;
        }
    })
};
// 绕Z轴旋转
function rotateByZ(angle, z) {
    const even = [{x: 0, y: 0}, {x: 2, y: 0}, {x: 2, y: 2}, {x: 0, y: 2}];
    const odd = [{x: 0, y: 1}, {x: 1, y: 0}, {x: 2, y: 1}, {x: 1, y: 2}];
    const parity = [even, odd];
    center.childNodes[0].childNodes.forEach( el => {
        if(el.position.z != z) return;
        // 旋转
        el.style.transition = 'all .5s ease';
        el.angle = angle;
        el.childNodes.forEach(element => {
            element.angle = angle;
            element.turnSide = 'ZSide';
        })
        el.style.transform =
            `translate3d(${coordinate[el.position.x]}rem, ${coordinate[el.position.y]}rem, ${coordinate[el.position.z]}rem) rotateZ(${el.angle * 90}deg)`;
        // // 改变eachCenter的position
        let stamp = (el.position.x + el.position.y) % 2;
        let item = parity[stamp].find( item => {
            return item.x == el.position.x && item.y == el.position.y;
        })
        // 排除中心点
        if(item) {
            let index = parity[stamp].indexOf(item);
            el.position.x = parity[stamp][(index + angle + 4) % 4].x;
            el.position.y = parity[stamp][(index + angle + 4) % 4].y;
        }
    })
};